chore: move api to api folder and seperate the types into another file

This commit is contained in:
Koosha Lahouti
2025-08-15 15:21:34 +03:30
parent 5a72b597c8
commit 0b42f4ccba
22 changed files with 845 additions and 1402 deletions

View File

@@ -1,8 +1,9 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useMemo } from 'react';
import {
Box,
Typography,
Button,
CircularProgress,
useTheme,
useMediaQuery,
} from '@mui/material';
@@ -10,9 +11,13 @@ import { useTranslation } from 'react-i18next';
import { CardContainer } from '@/components/CardContainer';
import { PageWrapper } from '../PageWrapper';
import { PasswordDialog } from './PasswordDialog';
import { Toast } from '@/components/Toast';
import { regex } from '@/utils/regex';
import apiClient from '@/lib/apiClient';
import { useManualApi } from '@/hooks/useApi';
import {
addPassword,
changePassword,
fetchProfile,
} from '../../api/settingsApi';
export function PasswordSecurity() {
const theme = useTheme();
@@ -25,158 +30,161 @@ export function PasswordSecurity() {
const [currentPassword, setCurrentPassword] = useState('');
const [showValidation, setShowValidation] = useState(false);
const [showPasswordAlert, setShowPasswordAlert] = useState(false);
const [changePassword, setChangePassword] = useState(false);
const [loading, setLoading] = useState(false);
const [changePasswordUI, setChangePasswordUI] = useState(false);
const { validPassword } = regex(password);
const matchPassword = password === confirmPassword;
const {
hasNumber,
hasMinLength,
hasUpperAndLower,
hasSpecialChar,
validPassword,
} = regex(password);
data: profileData,
loading: isFetching,
error: fetchError,
execute: executeFetchProfile,
} = useManualApi(fetchProfile);
const matchPassword = password === confirmPassword;
const {
data: addData,
loading: isAdding,
error: addError,
execute: executeAddPassword,
} = useManualApi(addPassword);
const {
data: changeData,
loading: isChanging,
error: changeError,
execute: executeChangePassword,
} = useManualApi(changePassword);
useEffect(() => {
executeFetchProfile();
}, []);
useEffect(() => {
if (profileData?.success) {
setChangePasswordUI(profileData.hasPassword || false);
}
}, [profileData]);
useEffect(() => {
if (password) {
if (!validPassword) {
setShowValidation(true);
} else {
const timer = setTimeout(() => setShowValidation(false), 1000);
return () => clearTimeout(timer);
}
setShowValidation(!validPassword);
} else {
setShowValidation(false);
}
}, [password, validPassword]);
useEffect(() => {
if (addData?.success || changeData?.success) {
setShowPasswordAlert(true);
if (!changePasswordUI) {
setChangePasswordUI(true);
}
setOpen(false);
setPassword('');
setConfirmPassword('');
setCurrentPassword('');
}
}, [addData, changeData, changePasswordUI]);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const handleShowAlert = async () => {
try {
setLoading(true);
const url = changePassword
? 'Profile/ChangePassword'
: 'Profile/AddPassword';
const payload = changePassword
? {
oldPassword: currentPassword,
newPassword: password,
}
: {
password,
// confirmPassword,
};
const res = await apiClient.post(url, payload, {
headers: {
Authorization: `Bearer ${localStorage.getItem('authToken')}`,
'Content-Type': 'application/json',
},
const handlePasswordSubmit = () => {
if (changePasswordUI) {
executeChangePassword({
oldPassword: currentPassword,
newPassword: password,
});
if (res.data?.success) {
setShowPasswordAlert(true);
if (!changePassword) setChangePassword(true);
handleClose();
setPassword('');
setConfirmPassword('');
setCurrentPassword('');
} else {
console.error('Password update failed:', res.data?.message);
}
} catch (err) {
console.error('Error updating password:', err);
} finally {
setLoading(false);
} else {
executeAddPassword({ password });
}
};
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
title={t('securityForm.password')}
subtitle={t('securityForm.determinePassword')}
action={
<Button
variant="outlined"
onClick={handleOpen}
sx={{
backgroundColor: 'primary.main',
color: 'background.paper',
textTransform: 'none',
}}
>
{changePassword
<Button variant="contained" onClick={handleOpen}>
{changePasswordUI
? t('securityForm.changePassword')
: t('securityForm.addPassword')}
</Button>
}
>
<Box
sx={{
px: { xs: 2, sm: 3, md: 4 },
py: 2,
display: 'flex',
flexDirection: 'column',
gap: 2,
bgcolor: 'background.paper',
flex: 1,
}}
>
{changePassword ? (
<Box sx={{ flexDirection: 'column', px: 2, py: 2 }}>
<Typography variant="h6">
{t('securityForm.activePassword')}
</Typography>
<Typography variant="caption" color="text.secondary">
{t('securityForm.lastChange')}
</Typography>
</Box>
) : (
<Typography
variant="body1"
color="text.secondary"
sx={{ textAlign: 'center', py: { xs: 4, sm: '43.5px' } }}
>
{t('securityForm.notDeterminedPassword')}
</Typography>
)}
<PasswordDialog
open={open}
fullScreen={fullScreen}
handleClose={handleClose}
password={password}
setPassword={setPassword}
confirmPassword={confirmPassword}
setConfirmPassword={setConfirmPassword}
currentPassword={currentPassword}
setCurrentPassword={setCurrentPassword}
showValidation={showValidation}
hasNumber={hasNumber}
hasMinLength={hasMinLength}
hasUpperAndLower={hasUpperAndLower}
hasSpecialChar={hasSpecialChar}
matchPassword={matchPassword}
loading={loading}
handleShowAlert={handleShowAlert}
changePassword={changePassword}
t={t}
/>
<Toast
color="success"
open={showPasswordAlert}
onClose={() => setShowPasswordAlert(false)}
{isFetching ? (
<Box
sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
p: 4,
minHeight: '120px',
}}
>
{t('securityForm.alertSuccess')}
</Toast>
</Box>
<CircularProgress />
</Box>
) : fetchError ? (
<Box sx={{ textAlign: 'center', p: 4 }}>
<Typography color="error.main">
{getErrorMessage(fetchError)}
</Typography>
</Box>
) : (
<Box sx={{ px: 4, py: 2 }}>
{changePasswordUI ? (
<Box>
<Typography variant="h6">
{t('securityForm.activePassword')}
</Typography>
<Typography variant="caption" color="text.secondary">
{t('securityForm.lastChange')}
</Typography>
</Box>
) : (
<Typography
variant="body1"
color="text.secondary"
sx={{ textAlign: 'center', py: 4 }}
>
{t('securityForm.notDeterminedPassword')}
</Typography>
)}
<PasswordDialog
open={open}
fullScreen={fullScreen}
handleClose={handleClose}
password={password}
setPassword={setPassword}
confirmPassword={confirmPassword}
setConfirmPassword={setConfirmPassword}
currentPassword={currentPassword}
setCurrentPassword={setCurrentPassword}
showValidation={showValidation}
validPassword={validPassword}
matchPassword={matchPassword}
loading={isAdding || isChanging}
handleSubmit={handlePasswordSubmit}
changePassword={changePasswordUI}
apiError={getErrorMessage(apiError)}
t={t}
/>
</Box>
)}
</CardContainer>
</PageWrapper>
);