Skip to content

Commit

Permalink
add validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikita-Pashin committed Nov 29, 2023
1 parent e090540 commit 1478406
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 14 deletions.
7 changes: 6 additions & 1 deletion public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,10 @@
"Change": "Change",
"Cancel": "Cancel",
"Save": "Save",
"Loading": "Loading"
"Loading": "Loading",
"Server error": "Server error",
"Incorrect user data": "Incorrect user data",
"Incorrect user age": "Incorrect user age",
"Incorrect user country": "Incorrect user country",
"No data": "No data"
}
7 changes: 6 additions & 1 deletion public/locales/ru/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,10 @@
"Change": "Изменить",
"Cancel": "Отмена",
"Save": "Сохранить",
"Loading": "Загрузка"
"Loading": "Загрузка",
"Server error": "Серверная ошибка",
"Incorrect user data": "Неверные данные пользователя",
"Incorrect user age": "Неверный возраст пользователя",
"Incorrect user country": "Неверная страна пользователя",
"No data": "Нет данных"
}
1 change: 1 addition & 0 deletions src/entities/Profile/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export { getProfileFormUsername } from './model/selectors/getProfileFormUsername
export { getProfileFormCity } from './model/selectors/getProfileFormCity/getProfileFormCity';
export { getProfileIsLoading } from './model/selectors/getProfileIsLoading/getProfileIsLoading';
export { getProfileReadOnly } from './model/selectors/getProfileReadOnly/getProfileReadOnly';
export { getProfileValidateErorrs } from './model/selectors/getProfileValidateErorrs/getProfileValidateErorrs';
export { EditableProfile } from './ui/EditableProfile/EditableProfile';
export { EditableProfileButtons } from './ui/EditableProfileButtons/EditableProfileButtons';
export { Profile, ProfileSchema } from './model/types/profile';
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { DeepPartial } from '@reduxjs/toolkit';
import { StateSchema } from 'app/providers/StoreProvider';
import { getProfileValidateErorrs } from './getProfileValidateErorrs';
import { ValidateProfileError } from '../../types/profile';

describe('getProfileValidateErorrs.test', () => {
test('should return value', () => {
const state: DeepPartial<StateSchema> = {
profile: {
validateError: [
ValidateProfileError.INCORRECT_COUNTRY,
ValidateProfileError.INCORRECT_USER_DATA,
],
},
};

expect(getProfileValidateErorrs(state as StateSchema)).toEqual([
ValidateProfileError.INCORRECT_COUNTRY,
ValidateProfileError.INCORRECT_USER_DATA,
]);
});

test('should work with empty state', () => {
const state: DeepPartial<StateSchema> = {};

expect(getProfileValidateErorrs(state as StateSchema)).toEqual(undefined);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { StateSchema } from 'app/providers/StoreProvider';

export const getProfileValidateErorrs = (state: StateSchema) => state.profile?.validateError;
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import { createAsyncThunk } from '@reduxjs/toolkit';
import { StateSchema, ThunkConfig } from 'app/providers/StoreProvider';
import { Profile } from '../../types/profile';
import { Profile, ValidateProfileError } from '../../types/profile';
import { getProfileForm } from '../../selectors/getProfileForm/getProfileForm';
import { validateProfileData } from '../validateProfileData/validateProfileData';

export const updateProfileData = createAsyncThunk<
Profile,
void,
ThunkConfig<string>
ThunkConfig<ValidateProfileError[]>
>(
'profile/updateProfileData',
async (_, thunkApi) => {
const { extra, rejectWithValue, getState } = thunkApi;

const formData = getProfileForm(getState() as StateSchema);

const errors = validateProfileData(formData);

if (errors.length) {
return rejectWithValue(errors);
}

try {
const response = await extra.api.put<Profile>('/profile', formData);

return response.data;
} catch (e) {
return rejectWithValue('error');
return rejectWithValue([ValidateProfileError.SERVER_ERROR]);
}
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Profile, ValidateProfileError } from '../../types/profile';

export const validateProfileData = (profile: Profile) => {
if (!profile) {
return [ValidateProfileError.NO_DATA];
}

const {
country, age, firstName, secondName,
} = profile;
const error: ValidateProfileError[] = [];

if (!firstName || !secondName) {
error.push(ValidateProfileError.INCORRECT_USER_DATA);
}

if (!country) {
error.push(ValidateProfileError.INCORRECT_COUNTRY);
}

if (!age) {
error.push(ValidateProfileError.INCORRECT_AGE);
}

return error;
};
8 changes: 5 additions & 3 deletions src/entities/Profile/model/slice/profileSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const profileSlice = createSlice({
cancelEdit: (state) => {
state.readOnly = true;
state.form = state.data;
state.validateError = undefined;
},
updateProfileForm: (state, { payload }: PayloadAction<Profile>) => {
state.form = {
Expand Down Expand Up @@ -47,14 +48,15 @@ const profileSlice = createSlice({
state.data = payload;
state.form = payload;
state.readOnly = true;
state.validateError = undefined;
},
[updateProfileData.pending.type]: (state) => {
state.isLoading = true;
state.error = undefined;
state.validateError = undefined;
},
[updateProfileData.rejected.type]: (state) => {
[updateProfileData.rejected.type]: (state, action) => {
state.isLoading = false;
state.error = 'There was an error update the profile';
state.validateError = action.payload;
},
},
});
Expand Down
9 changes: 9 additions & 0 deletions src/entities/Profile/model/types/profile.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
export enum ValidateProfileError {
NO_DATA = 'No data',
SERVER_ERROR = 'Server error',
INCORRECT_USER_DATA = 'Incorrect user data',
INCORRECT_AGE = 'Incorrect user age',
INCORRECT_COUNTRY = 'Incorrect user country',
}

export interface Profile {
email?: string;
firstName?: string;
Expand All @@ -14,4 +22,5 @@ export interface ProfileSchema {
isLoading: boolean;
error?: string;
readOnly: boolean;
validateError?: ValidateProfileError[];
}
6 changes: 5 additions & 1 deletion src/entities/Profile/ui/EditableProfile/EditableProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
getProfileDataCountry, getProfileDataCity, getProfileDataUsername,
getProfileDataSecondName, getProfileDataFirstName, getProfileIsLoading, getProfileReadOnly,
getProfileFormEmail, getProfileFormAge, getProfileFormFirstName, getProfileFormSecondName,
getProfileFormUsername, getProfileFormCountry, getProfileFormCity,
getProfileFormUsername, getProfileFormCountry, getProfileFormCity, getProfileValidateErorrs,
} from 'entities/Profile';
import s from './EditableProfile.module.scss';

Expand All @@ -34,6 +34,7 @@ export const EditableProfile = () => {

const isLoading = useSelector(getProfileIsLoading);
const readOnly = useSelector(getProfileReadOnly);
const validateErrors = useSelector(getProfileValidateErorrs);

const email = readOnly ? dataEmail : formEmail;
const age = readOnly ? dataAge : formAge;
Expand Down Expand Up @@ -150,6 +151,9 @@ export const EditableProfile = () => {
placeholder={readOnly ? 'City missing' : 'Enter your city'}
readOnly={readOnly}
/>
{validateErrors && validateErrors.map((err) => (
<div key={err}>{t(err)}</div>
))}
</div>
);
};
10 changes: 5 additions & 5 deletions src/shared/ui/Typography/Typography.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC } from 'react';
import { FC, memo } from 'react';
import classNames from 'classnames';
import s from './Typography.module.scss';

Expand All @@ -13,7 +13,7 @@ interface TypographyConstructorProps {

type TypographyProps = Omit<TypographyConstructorProps, 'variant'>;

const Typography: FC<TypographyConstructorProps> = ({
const Typography: FC<TypographyConstructorProps> = memo(({
variant, children, bold, as = 'span', className, ...restProps
}) => {
const Component = as;
Expand All @@ -26,7 +26,7 @@ const Typography: FC<TypographyConstructorProps> = ({
{children}
</Component>
);
};
});

export const TextXS: FC<TypographyProps> = ({
children, ...restProps
Expand Down Expand Up @@ -94,7 +94,7 @@ export const TextXXL: FC<TypographyProps> = ({
</Typography>
);

export const TextXXXL: FC<TypographyProps> = ({
export const TextXXXL: FC<TypographyProps> = memo(({
children, ...restProps
}) => (
<Typography
Expand All @@ -103,4 +103,4 @@ export const TextXXXL: FC<TypographyProps> = ({
>
{children}
</Typography>
);
));

0 comments on commit 1478406

Please sign in to comment.